﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Projektarbete
{
    public enum NotificationLifetime { AddItem = 120, RemoveItem = 120, PlayerHurt = 180, PlayerHealed = 180, Info = 180 }; //Time in frames to display the message
    public enum NotificationColor : uint { AddItem = 0xffffffff, RemoveItem = 0xffffffff, PlayerHurt = 0xc70000ff, PlayerHealed = 0x00c700ff, Info  = 0xffffffff}; //Colors as hex-values

    class Notification
    {
        public string text;

        public Color color;
        public float alpha;

        public ushort lifeTime;

        /// <summary>
        /// Create a new notification. Lifetime set to 300 (5 seconds). Color set to white
        /// </summary>
        /// <param name="text">text to display</param>
        public Notification(string text)
        {
            this.text = text;
            color = Color.White;

            lifeTime = 300;
            alpha = 1;
        }

        /// <summary>
        /// Create a new notification. Color set to white
        /// </summary>
        /// <param name="text">text to display</param>
        /// <param name="color">color to use</param>
        public Notification(string text, ushort lifeTime)
        {
            this.text = text;
            color = Color.White;

            this.lifeTime = lifeTime;
            alpha = 1;
        }

        /// <summary>
        /// Create a new notification. Color set to white
        /// </summary>
        /// <param name="text">text to display</param>
        /// <param name="color">color to use</param>
        public Notification(string text, NotificationLifetime lifeTime)
        {
            this.text = text;
            color = Color.White;

            this.lifeTime = (ushort)lifeTime;
            alpha = 1;
        }

        /// <summary>
        /// Create a new notification. Lifetime set to 300 (5 seconds)
        /// </summary>
        /// <param name="text">text to display</param>
        /// <param name="color">color to use</param>
        public Notification(string text, Color color)
        {
            this.text = text;
            this.color = color;

            lifeTime = 300;
            alpha = 1;
        }

        /// <summary>
        /// Create a new notification. Lifetime set to 300 (5 seconds)
        /// </summary>
        /// <param name="text">text to display</param>
        /// <param name="color">color to use</param>
        public Notification(string text, NotificationColor color)
        {
            this.text = text;
            this.color = Color.White;
            this.color.PackedValue = (uint)color;

            lifeTime = 300;
            alpha = 1;
        }

        /// <summary>
        /// Create a new notification
        /// </summary>
        /// <param name="text">text to display</param>
        /// <param name="color">color to use</param>
        /// <param name="lifeTime">lifetime (in frames)</param>
        public Notification(string text, Color color, ushort lifeTime)
        {
            this.text = text;
            this.color = color;

            this.lifeTime = lifeTime;
            alpha = 1;
        }

        /// <summary>
        /// Create a new notification
        /// </summary>
        /// <param name="text">text to display</param>
        /// <param name="color">color to use</param>
        /// <param name="lifeTime">lifetime (in frames)</param>
        public Notification(string text, NotificationColor color, ushort lifeTime)
        {
            this.text = text;
            this.color = Color.White;
            this.color.PackedValue = (uint)color;

            this.lifeTime = lifeTime;
            alpha = 1;
        }

        /// <summary>
        /// Create a new notification
        /// </summary>
        /// <param name="text">text to display</param>
        /// <param name="color">color to use</param>
        /// <param name="lifeTime">lifetime (in frames)</param>
        public Notification(string text, Color color, NotificationLifetime lifeTime)
        {
            this.text = text;
            this.color = color;

            this.lifeTime = (ushort)lifeTime;
            alpha = 1;
        }

        /// <summary>
        /// Create a new notification
        /// </summary>
        /// <param name="text">text to display</param>
        /// <param name="color">color to use</param>
        /// <param name="lifeTime">lifetime (in frames)</param>
        public Notification(string text, NotificationColor color, NotificationLifetime lifeTime)
        {
            this.text = text;
            this.color = Color.White;
            this.color.PackedValue = (uint)color;

            this.lifeTime = (ushort)lifeTime;
            alpha = 1;
        }
    }

    static class HUD
    {
        private static Texture2D background; //Bakgrund till HUD:en på höger sida
        private static Texture2D white;

        private static Vector2 drawPosition;
        private static Rectangle minimapDrawPosition;
        private static Rectangle scissorRectangle;

        private static Texture2D minimap;

        private static Vector2 tempVector2;
        private static Point tempPoint;
        private static Rectangle tempRectangle;

        private static bool[][] minimapVisitedMap;
        private static Color[][] minimapInfoMap;
        private static Point playerTiledPos;

        private static string playerName;
        private static string playerGold;
        private static string playerHealth;
        private static string playerMana;

        private static ushort armor;
        private static ushort agility;
        private static ushort strength;

        #region notifications
        private static LinkedList<Notification> notifications; //För notifikationer
        private static LinkedList<Notification> queuedNotifications; //Notifikationer som inte får plats på skärmen men som ska visas
        private static LinkedList<Notification> removeNotifications; //för att ta bort notifikationer ur listor

        private static SpriteFont notificationFont; //font för notifikationer

        private static bool drawNotification; //Ska den rita någon notifikation?
        #endregion

        /// <summary>
        /// Initializes everything necessary for the HUD to work
        /// </summary>
        public static void Initialize(GraphicsDevice graphicsDevice)
        {
            background = Global.LoadTexture2D("HUD/mainHUD");
            white = Global.White;
            notificationFont = Global.LoadSpriteFont("Fonts/notificationFont");

            drawPosition = new Vector2(800, 0);
            minimapDrawPosition = new Rectangle(805, 100, MapHandler.mapDimensions.X * 4, MapHandler.mapDimensions.Y * 4);
            scissorRectangle = new Rectangle(minimapDrawPosition.X, minimapDrawPosition.Y, 192, 192);

            minimapVisitedMap = new bool[48][];
            minimapInfoMap = new Color[48][];

            for (int y = 0; y < 48; y++) //192 / 4 (minimap.width / minimap.pixelsize)
            {
                minimapVisitedMap[y] = new bool[48];
                minimapInfoMap[y] = new Color[48];

                for (int x = 0; x < 48; x++)
                {
                    minimapVisitedMap[y][x] = false;
                    minimapInfoMap[y][x] = Color.White * 0.0f;
                }
            }

            minimap = new Texture2D(graphicsDevice, MapHandler.mapDimensions.X, MapHandler.mapDimensions.Y);

            Color[] minimapColor = new Color[MapHandler.mapDimensions.X * MapHandler.mapDimensions.Y];

            for (int y = 0; y < MapHandler.mapDimensions.Y; y++)
            {
                for (int x = 0; x < MapHandler.mapDimensions.X; x++)
                {
                    switch (MapHandler.map[y][x].getTextureID)
                    {
                        case 1:
                        case 2:
                        case 3:
                        case 4:
                        case 5:
                        case 6:
                        case 7:
                        case 8:
                        case 9:
                            minimapColor[y * MapHandler.mapDimensions.X + x] = Color.White;
                            break;
                        case 10:
                        case 11:
                        case 12:
                        case 13:
                        case 14:
                        case 15:
                        case 16:
                        case 17:
                        case 18:
                        case 19:
                            minimapColor[y * MapHandler.mapDimensions.X + x] = Color.Gray;
                            break;
                        case 20:
                        case 21:
                            minimapColor[y * MapHandler.mapDimensions.X + x] = Color.Aqua;
                            break;
                        default:
                            break;
                    }
                }
            }

            minimap.SetData(minimapColor);

            //Player stuff
            playerName = Player.getName;
            playerGold = Player.gold.ToString();
            playerHealth = Player.getHealth.ToString();
            playerMana = Player.getHealth.ToString();

            armor = 0;
            agility = 0;
            strength = 0;

            #region notifications
            notifications = new LinkedList<Notification>();
            queuedNotifications = new LinkedList<Notification>();
            removeNotifications = new LinkedList<Notification>();

            drawNotification = false;
            #endregion
        }

        /// <summary>
        /// To be called every frame
        /// </summary>
        public static void Update()
        {
            #region notifications
            removeNotifications.Clear();
            drawNotification = false;

            if (notifications.Count > 0)
            {
                drawNotification = true;

                foreach (Notification notification in notifications)
                    if (notification.lifeTime <= 0)
                        removeNotifications.AddLast(notification); //Lägg till den här notifikationen i remove-listan för att senare ta bort den
                    else
                    {
                        if (notification.lifeTime < 80)
                            notification.alpha = (notification.lifeTime + 20) * 0.01f; //Gör så att notifikationen fade-ar ut

                        notification.lifeTime--;
                    }

                if (removeNotifications.Count > 0)
                    foreach (Notification notification in removeNotifications)
                        notifications.Remove(notification); //Ta bort notifikationer från rita-ut-listan
            }

            if (queuedNotifications.Count > 0) //Om det är någon notifikation som inte får plats på skärmen
            {
                if (notifications.Count < 10) //Om den får plats på skärmen
                {
                    foreach (Notification notification in queuedNotifications)
                    {
                        if (notifications.Count < 10)
                        {
                            notifications.AddLast(notification); //Lägg till den här notifikationen om den får plats på skärmen
                            removeNotifications.AddLast(notification); //Lägg till den här i remove-listan för att ta bort den senare
                        }
                        else
                            break;
                    }

                    foreach (Notification notification in removeNotifications)
                        queuedNotifications.Remove(notification); //Ta bort från kö-listan
                }
            }
            #endregion
        }

        /// <summary>
        /// Adds a notification to the queue
        /// </summary>
        /// <param name="notification">Notification to add</param>
        public static void AddNotification(Notification notification)
        {
            queuedNotifications.AddLast(notification);
        }

        /// <summary>
        /// Updates the minimap
        /// </summary>
        /// <param name="playerPosition">TILED player position. OBS!!! TILED!</param>
        public static void RefreshMinimap(Point playerPosition)
        {
            //805 + 96 = 901 (minimap.X + minimap.width/2)
            minimapDrawPosition.Location = new Point(901 - playerPosition.X * 4, 196 - playerPosition.Y * 4);

            //805 + 192 = 997 (minimap.X + minimap.width)
            minimapDrawPosition.X = (int)MathHelper.Clamp(minimapDrawPosition.X, 997 - MapHandler.mapDimensions.X * 4, 805);
            minimapDrawPosition.Y = (int)MathHelper.Clamp(minimapDrawPosition.Y, 292 - MapHandler.mapDimensions.Y * 4, 100);

            tempPoint.X = (int)MathHelper.Clamp(playerPosition.X, 24, MapHandler.mapDimensions.X - 24);
            tempPoint.Y = (int)MathHelper.Clamp(playerPosition.Y, 24, MapHandler.mapDimensions.Y - 24);

            for (int y = tempPoint.Y; y < tempPoint.Y + 48; y++)
            {
                for (int x = tempPoint.X; x < tempPoint.X + 48; x++)
                {
                    minimapVisitedMap[y - tempPoint.Y][x - tempPoint.X] = MapHandler.visitedMap[y - 24][x - 24];
                }
            }
            //TODO: PUT PLAYER ON MINIMAP
        }

        /// <summary>
        /// Draws the HUD and any notifications. To be called every draw-call
        /// </summary>
        /// <param name="spriteBatch">spriteBatch</param>
        /// <param name="graphicsDevice">graphicsDevice</param>
        public static void Draw(SpriteBatch spriteBatch, GraphicsDevice graphicsDevice)
        {
            spriteBatch.Draw(background, drawPosition, Color.White);

            spriteBatch.End();

            graphicsDevice.RasterizerState = new RasterizerState() { ScissorTestEnable = true };
            graphicsDevice.ScissorRectangle = scissorRectangle;

            spriteBatch.Begin(SpriteSortMode.Immediate, null, SamplerState.PointClamp, null, graphicsDevice.RasterizerState);

            spriteBatch.Draw(minimap, minimapDrawPosition, Color.White);

            tempRectangle.Width = 4;
            tempRectangle.Height = 4;

            for (int y = 0; y < 48; y++)
            {
                for (int x = 0; x < 48; x++)
                {
                    tempRectangle.X = 805 + x * 4;
                    tempRectangle.Y = 100 + y * 4;

                    if (!minimapVisitedMap[y][x])
                        spriteBatch.Draw(white, tempRectangle, Color.Black);

                    spriteBatch.Draw(white, tempRectangle, minimapInfoMap[y][x]);
                }
            }

            spriteBatch.End();
            spriteBatch.Begin();

            tempVector2.X = 862;
            tempVector2.Y = 3;

            spriteBatch.DrawString(notificationFont, playerName, tempVector2, Color.White);

            tempVector2.Y = 19;

            spriteBatch.DrawString(notificationFont, playerGold, tempVector2, Color.White);

            tempVector2.Y = 35;

            spriteBatch.DrawString(notificationFont, playerHealth, tempVector2, Color.White);

            tempVector2.Y = 52;

            spriteBatch.DrawString(notificationFont, playerMana, tempVector2, Color.White);

            tempVector2.Y = 450;
            tempVector2.X = 865;
            
            spriteBatch.DrawString(notificationFont, strength.ToString(), tempVector2, Color.White);

            tempVector2.Y = 468;

            spriteBatch.DrawString(notificationFont, agility.ToString(), tempVector2, Color.White);

            #region notifications
            if (drawNotification)
            {
                byte iteration = 1; //Används för att stapla notifikationer. Kolla nedanför

                foreach (Notification notification in notifications)
                {
                    spriteBatch.DrawString(notificationFont, notification.text, new Vector2(0, 600 - iteration * 24), notification.color * notification.alpha);
                    iteration++;
                }
            }
            #endregion
        }

        public static void UpdateHealth()
        {
            playerHealth = Player.getHealth.ToString();
        }

        public static void UpdateMana()
        {
            playerMana = Player.getMana.ToString();
        }

        public static void UpdateGold()
        {
            playerGold = Player.gold.ToString();
        }

        internal static void UpdateStats()
        {
            strength = Player.Strength;
            agility = Player.Agility;
        }
    }
}